/*
 * XWizardDialog.java
 * 
 * Copyright (c) 2006 - 2007
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.xfc.wizard;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

import org.xfc.XApp;
import org.xfc.XAppFrame;
import org.xfc.XComponentFactory;
import org.xfc.components.XDefaultAction;
import org.xfc.components.XMultilineLabel;
import org.xfc.dialog.XDialog;

import com.jgoodies.forms.layout.FormLayout;

// TODO: wizards shouldn't necessarily be model


// TODO: should the close button instead say cancel?


// TODO: add an optional help button



/**
 * The main wizard class. In general, developers should not have to subclass a XWizardDialog.
 * 
 * @author Devon Carew
 */
public class XWizardDialog
	extends XDialog
{
	/**
	 * A 16x16 default wizard icon.
	 */
	public static final ImageIcon DEFAULT_WIZARD_ICON = XApp.getApp().getResources().getIcon("/eclipse/misc/default_wizard.gif");
	
	/**
	 * A 75x66 default wizard image. Can be used the wizard page images.
	 */
	public static final ImageIcon DEFAULT_WIZARD_IMAGE = XApp.getApp().getResources().getIcon("/eclipse/org.eclipse.ui/icons/full/wizban/new_wiz.gif");
	
	
//	private XDefaultAction helpAction = new XDefaultAction("wizard.help") {
//		public void actionPerformed(ActionEvent event) { doHelp(); }
//	};
	
	private XDefaultAction prevAction = new XDefaultAction("wizard.back") {
		public void actionPerformed(ActionEvent event) { doPrev(); }
	};

	private XDefaultAction nextAction = new XDefaultAction("wizard.next") {
		public void actionPerformed(ActionEvent event) { doNext(); }
	};
	
	private XDefaultAction finishAction = new XDefaultAction("wizard.finish") {
		public void actionPerformed(ActionEvent event) { doFinish(); }
	};
	
	private JLabel 				pageImageLabel = new JLabel();
	private JLabel 				pageTitleLabel = new JLabel();
	private XMultilineLabel		pageDescriptionLabel = new XMultilineLabel();
	private JPanel				pagePanel = new JPanel(new BorderLayout());
	
	private XWizardListener		wizardListener;
	
	private List				pages = new ArrayList();
	private XWizardPage			currentPage;
	
	private Map					userData = new HashMap();
	private Stack				pageHistory = new Stack();
	
	private PropertyChangeListener pageListener = new PropertyChangeListener() {
		public void propertyChange(PropertyChangeEvent event) {
			updateCurrentPage();
		}
	};
	
	public XWizardDialog()
	{
		this(XApp.getApp());
	}
	
	public XWizardDialog(XApp app)
	{
		this(app.getActiveAppFrame());
	}
	
	public XWizardDialog(XAppFrame appFrame)
	{
		super((Frame)appFrame);
		
		initUI();
	}
	
	private void initUI()
	{
		setMinimumSize(new Dimension(550, 425));
		
		JPanel contents = new JPanel(new FormLayout(
			"pref:grow", 
			"pref 10px pref:grow"
		));
		
		// Create top panel.
		JPanel topPanel = new JPanel(new FormLayout(
			"4dlu pref 6dlu 10dlu pref:grow 4dlu", 
			"4dlu pref 3dlu pref:grow 4dlu"
		));
		topPanel.setBackground(Color.WHITE);
		topPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		
		// TEMP
		pageImageLabel.setIcon(DEFAULT_WIZARD_IMAGE);
		// TEMP
		
		JLabel bkImageLabel = new JLabel(XApp.getApp().getResources().getIcon("/eclipse/misc/wizard_page.gif"));
		pageTitleLabel.setFont(pageTitleLabel.getFont().deriveFont(Font.BOLD));
		pageDescriptionLabel.setPreferredWidth(380);
		pageImageLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		
		topPanel.add(pageImageLabel, 		"2, 2, 1, 3, c, t");
		topPanel.add(pageTitleLabel, 		"4, 2, 2, 1, f, f");
		topPanel.add(pageDescriptionLabel,	"5, 4, l, t");
		topPanel.add(bkImageLabel, 			"1, 1, 6, 5, r, b");
		
		contents.add(topPanel, 	"1, 1, f, f");
		contents.add(pagePanel, "1, 3, f, f");
		
		setContents(contents);
		
		
		XComponentFactory componentFactory = XApp.getComponentFactory();
		
		//componentFactory.applyAction(helpAction);
		componentFactory.applyAction(prevAction);
		componentFactory.applyAction(nextAction);
		componentFactory.applyAction(finishAction);
		
		//addAction(helpAction, XDialog.HELP_POSITION);
		addAction(prevAction, XDialog.OK_POSITION);
		addAction(nextAction, XDialog.OK_POSITION);
		addAction(finishAction, XDialog.OK_POSITION, true);
		setCloseAction(new AbstractAction() {
			public void actionPerformed(ActionEvent event) {
				doCancel();
			}
		});
		
		construct();
	}
	
	public void addWizardPage(XWizardPage wizardPage)
	{
		pages.add(wizardPage);
	}
	
	public XWizardListener getWizardListener()
	{
		return wizardListener;
	}

	public void setWizardListener(XWizardListener wizardListener)
	{
		this.wizardListener = wizardListener;
	}

	public void show()
	{
		resetWizard();
		
		super.show();
	}
	
	
	private void resetWizard()
	{
		if (currentPage != null)
			currentPage.removePropertyChangeListener(pageListener);
		
		currentPage = null;
		
		pageHistory.clear();
		
		if (pages.size() == 0)
			throw new IllegalStateException("wizards need at least one page");
		
		showPage((XWizardPage)pages.get(0));
	}
	
	private void showPage(XWizardPage wizardPage)
	{
		// Stop listening to the old page
		if (currentPage != null)
			currentPage.removePropertyChangeListener(pageListener);
		
		// start listening to the new page
		currentPage = wizardPage;
		
		if (currentPage != null)
			currentPage.addPropertyChangeListener(pageListener);
		
		// display the new page
		pagePanel.removeAll();
		
		if (currentPage != null)
			pagePanel.add(currentPage.getContent(), BorderLayout.CENTER);
		
		updateCurrentPage();
		
		pagePanel.validate();
		pagePanel.repaint();
	}
	
	private void updateCurrentPage()
	{
		if (currentPage == null)
		{
			pageTitleLabel.setText(null);
			pageDescriptionLabel.setText(null);
			
			nextAction.setEnabled(false);
			finishAction.setEnabled(false);
			
			prevAction.setEnabled(pageHistory.size() > 0);
			
			setErrorMessage(null);
		}
		else
		{
			pageTitleLabel.setText(currentPage.getTitle());
			pageDescriptionLabel.setText(currentPage.getDescription());
			
			XWizardPage nextPage = currentPage.getNextPage();
			
			if (currentPage.hasValidationError())
			{
				nextAction.setEnabled(false);
				finishAction.setEnabled(false);
			}
			else
			{
				nextAction.setEnabled(nextPage != null);
				finishAction.setEnabled(nextPage == null || currentPage.isEarlyFinishAllowed());
			}
			
			prevAction.setEnabled(pageHistory.size() > 0);
			
			setErrorMessage(currentPage.getValidationMessage());
		}
	}
	
//	private void doHelp()
//	{
//		
//		
//	}
	
	private void doPrev()
	{
		if (currentPage == null)
			return;
		
		XWizardPage prevPage = (XWizardPage)pageHistory.pop();
		
		showPage(prevPage);
	}
	
	private void doNext()
	{
		if (currentPage == null)
			return;
		
		XWizardPage nextPage = currentPage.getNextPage();
		
		// Record this in the history (for the prev action).
		pageHistory.push(currentPage);
		
		showPage(nextPage);
	}
	
	private void doFinish()
	{
		hide();
		
		if (wizardListener != null)
			wizardListener.wizardFinished(this);
	}
	
	private void doCancel()
	{
		hide();
		
		if (wizardListener != null)
			wizardListener.wizardCancelled(this);
	}
	
	
	public Object getUserData(Object key)
	{
		return userData.get(key);
	}
	
	public void setUserData(Object key, Object value)
	{
		userData.put(key, value);
	}
	
	public List getWizardPages()
	{
		return pages;
	}
	
}
